#include "stdafx.h"
#include "FntCombo.h"
#include "resource.h"
// Download by http://www.codefans.net
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define FCB_IMAGEWIDTH 15

/////////////////////////////////////////////////////////////////////////////
// CFontComboBox

BEGIN_MESSAGE_MAP(CFontComboBox, CComboBox)
	//{{AFX_MSG_MAP(CFontComboBox)
	ON_CONTROL_REFLECT(CBN_CLOSEUP, OnCloseup)
	ON_WM_NCDESTROY()
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_TIMER()
	ON_WM_PAINT()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CFontComboBox message handlers

int CFontComboBox::CompareItem(LPCOMPAREITEMSTRUCT lpCompareItemStruct) 
{
	// return -1 = item 1 sorts before item 2
	// return 0 = item 1 and item 2 sort the same
	// return 1 = item 1 sorts after item 2

/*	CString str1, str2;
	GetLBText(lpCompareItemStruct->itemID1, str1);
	GetLBText(lpCompareItemStruct->itemID2, str2);

	if ( str1 < str2 )
	{
		return -1;
	}
	else if ( str1 == str2 )
	{
		return 0;
	}
	else
	{
		return 1;
	}
*/
	return 0;
}

void CFontComboBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) 
{
	ASSERT(lpDrawItemStruct->CtlType == ODT_COMBOBOX);

	// for easy use
	CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
	ASSERT(pDC); // attaching failed

	CRect rect(lpDrawItemStruct->rcItem);

	// draw focus rectangle
	if (lpDrawItemStruct->itemState & ODS_FOCUS)
		pDC->DrawFocusRect(rect);

	// save the context attributes
	int nIndexDC = pDC->SaveDC();

	CBrush brush;
	// draw the selection state
	if (lpDrawItemStruct->itemState & ODS_SELECTED)
	{
		brush.CreateSolidBrush(::GetSysColor(COLOR_HIGHLIGHT));
		pDC->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
	}
	else
	{
		brush.CreateSolidBrush(pDC->GetBkColor());
	}
	pDC->SetBkMode(TRANSPARENT);
	pDC->FillRect(rect, &brush);

	CString strCurFont;
	GetLBText(lpDrawItemStruct->itemID, strCurFont);

	m_imagelist.Draw(pDC, 0, CPoint(rect.left, rect.top), ILD_TRANSPARENT);

	int nX = rect.left; // x pos for MRU separater lines
	rect.left += FCB_IMAGEWIDTH + 2;

	// create font object
	LOGFONT lf;
	::ZeroMemory(&lf, sizeof(lf));
	_tcscpy(lf.lfFaceName, strCurFont);
	lf.lfCharSet = DEFAULT_CHARSET;
	CFont font;
	font.CreateFontIndirect(&lf);

	// if it is a symbol font, 
	// draw the font's facename using default font
	FCBDATA * pFCBData = NULL;
	if ( m_mapFont.Lookup(strCurFont, pFCBData) 
		&& pFCBData->lf.lfCharSet == SYMBOL_CHARSET )
	{
		pDC->TextOut(rect.left, rect.top + 3, strCurFont);
		SIZE size = pDC->GetTextExtent(strCurFont);
		rect.left += size.cx;
	}
	// now draw the text using created font, 
	CFont * pOldFont = pDC->SelectObject(&font);
	pDC->TextOut(rect.left, rect.top, strCurFont);
	// and clean up
	pDC->SelectObject(pOldFont);
	font.DeleteObject();

	// draw font MRU separator, a line like ==============
	if ( lpDrawItemStruct->itemID == (UINT)m_mapFontMRU.GetCount() - 1 )
	{
		pDC->MoveTo(nX, rect.bottom - 3);
		pDC->LineTo(rect.right, rect.bottom - 3);

		pDC->MoveTo(nX, rect.bottom - 1);
		pDC->LineTo(rect.right, rect.bottom -1);
	}

	// restore state of DC
	pDC->RestoreDC(nIndexDC);
}

BOOL CFontComboBox::EnumFont()
{
	LOGFONT lf;
	HDC hDC = ::GetWindowDC(NULL); // DC for entire screen
	ZeroMemory(&lf, sizeof(lf));

	//lf.lfCharSet = DEFAULT_CHARSET;
	lf.lfCharSet = GB2312_CHARSET;

	if (!EnumFontFamiliesEx(
			hDC, 	// handle to device context
			&lf, 	// pointer to logical font information
			(FONTENUMPROC)EnumScreenFontCallbackFn, 	// pointer to callback function
			(LPARAM) this, 	// application-supplied data
			(DWORD) 0))
		return FALSE;

	::ReleaseDC(NULL, hDC);	
	return TRUE;
}

BOOL CALLBACK AFX_EXPORT CFontComboBox::EnumScreenFontCallbackFn(ENUMLOGFONTEX* pelf, 
	NEWTEXTMETRICEX* /*lpntm*/, int FontType, LPVOID pThis)

{
	if (FontType & RASTER_FONTTYPE)
	{
		return 1;
	}

	LPCTSTR lpszFace = pelf->elfLogFont.lfFaceName;
	FCBDATA * pFCBData = NULL;

	if ( ! _tcschr(lpszFace, '@') && ! ((CFontComboBox*)pThis)->m_mapFont.Lookup(lpszFace, pFCBData))
	{
		pFCBData = new FCBDATA;
		pFCBData->flag = 0;
		pFCBData->lf = pelf->elfLogFont;

		((CFontComboBox*)pThis)->m_mapFont.SetAt(lpszFace, pFCBData);
	}

	return 1; // call me back next time
}

void CFontComboBox::Initialize(int nMRUCount /* =10 */)
{
	FCBDATA * pFCBData = NULL;
	CString strKey, strComp;

	// enumerate all fonts in system, excluding raster fonts
	EnumFont();
	ResetContent();

	int cxMax = 0;
	CDC* pDC = GetDC();
	CFont font;

	POSITION pos = m_mapFont.GetStartPosition();
	while (pos)
	{
		m_mapFont.GetNextAssoc(pos, strKey, pFCBData);

		// calculate width to set
		if ( pFCBData->lf.lfCharSet == SYMBOL_CHARSET)
		{
			pFCBData->lf.lfHeight = 0;
			pFCBData->lf.lfWidth = 0;
			font.CreateFontIndirect(&pFCBData->lf);
			CFont * pOldFont = pDC->SelectObject(&font);
			SIZE size = pDC->GetTextExtent(pFCBData->lf.lfFaceName, 
				_tcslen(pFCBData->lf.lfFaceName));
			pDC->SelectObject(pOldFont);
			font.DeleteObject();
			SIZE size2 = pDC->GetTextExtent(pFCBData->lf.lfFaceName, 
				_tcslen(pFCBData->lf.lfFaceName));
			if ( cxMax < size.cx + size2.cx)
			{
				cxMax = size.cx + size2.cx;
			}
		}
		else
		{
			SIZE size = pDC->GetTextExtent(pFCBData->lf.lfFaceName, 
				_tcslen(pFCBData->lf.lfFaceName));
			cxMax = size.cx > cxMax ? size.cx : cxMax;
		}
		AddString(strKey);
	}

	// set dropped down width to show all the characters
	SetDroppedWidth(cxMax);

	m_nMRUCount = nMRUCount;
	m_imagelist.Create(IDB_FONTTYPE, 15, 1, RGB(255, 0, 255));
}

void CFontComboBox::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct) 
{
	lpMeasureItemStruct->itemHeight = 20;
}

void CFontComboBox::OnCloseup() 
{
	int nSel = GetCurSel();
	if (nSel != CB_ERR)
	{
		CString strFont;
		GetLBText(nSel, strFont);
		SetFontMRU(strFont);
	}
}

void CFontComboBox::OnNcDestroy() 
{
	CComboBox::OnNcDestroy();
	m_imagelist.DeleteImageList();

	// clean up work to do, 
	// avoiding memory leaks
	FCBDATA * pFCBData;
	CString strKey;
	POSITION pos = m_mapFont.GetStartPosition();
	while (pos)
	{
		m_mapFont.GetNextAssoc(pos, strKey, pFCBData);
		delete pFCBData;
	}
}

void CFontComboBox::PreSubclassWindow() 
{
	CComboBox::PreSubclassWindow();
	UINT style = GetWindowLong(m_hWnd, GWL_STYLE);
	style |= CBS_SORT|CBS_OWNERDRAWVARIABLE;
	SetWindowLong(m_hWnd, GWL_STYLE, style);
//	SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER);
//	ModifyStyle(0, CBS_SORT|CBS_OWNERDRAWVARIABLE);
}

void CFontComboBox::SetFontMRU(const CString& strFont)
{
	FCBDATA * pFCBData = NULL;
	CString strFontDesc;
	
	// try to find the font, if not, exit function
	if ( m_mapFont.Lookup(strFont, pFCBData) )
	{
		if ( m_mapFontMRU.Lookup(strFont, pFCBData) )
		{
			int nSel = FindStringExact(-1, strFont );
			ASSERT(nSel != CB_ERR);
			if ( nSel != 0 )
			{
				DeleteString(nSel);
				int index = InsertString(0, strFont);
				ASSERT( index != CB_ERR );
				SetCurSel(index);
			}
		}
		else // not in MRU(s)
		{
			int nSel = FindStringExact(-1, strFont);
			if ( m_mapFontMRU.GetCount () >= m_nMRUCount )
			{
				CString str;
				GetLBText(m_nMRUCount - 1, str);
				DeleteString(m_nMRUCount - 1);

				m_mapFontMRU.RemoveKey(str);
			}
			if ( nSel != CB_ERR )
			{
				int index = InsertString(0, strFont);
				ASSERT(index != CB_ERR);
				SetCurSel(index);
				pFCBData->flag |= FCB_INMRU;
				m_mapFontMRU.SetAt(strFont, pFCBData);
			}
		}
	} // lookup in all fonts
}

/***************************************************************************\
*
* NOTE that the following funcitons is for the purpose of flat-drawing
* it could be removed if the flat effect is not desired
*
\***************************************************************************/

void CFontComboBox::DrawCombo(DWORD dwStyle, COLORREF clrTopLeft, COLORREF clrBottomRight)
{
	CRect rcItem;
	GetClientRect(&rcItem);
	CDC* pDC = GetDC();
	
	// Cover up dark 3D shadow.
	pDC->Draw3dRect(rcItem, clrTopLeft, clrBottomRight);
	rcItem.DeflateRect(1, 1);
	
	if (!IsWindowEnabled())
	{
		pDC->Draw3dRect(rcItem, ::GetSysColor(COLOR_BTNHIGHLIGHT), 
			::GetSysColor(COLOR_BTNHIGHLIGHT));
	}
	else
	{
		pDC->Draw3dRect(rcItem, ::GetSysColor(COLOR_BTNFACE), 
			::GetSysColor(COLOR_BTNFACE));
	}

	// Cover up dark 3D shadow on drop arrow.
	rcItem.DeflateRect(1, 1);
	rcItem.left = rcItem.right-Offset();
	pDC->Draw3dRect(rcItem, ::GetSysColor(COLOR_BTNFACE), 
		::GetSysColor(COLOR_BTNFACE));
	
	// Cover up normal 3D shadow on drop arrow.
	rcItem.DeflateRect(1, 1);
	pDC->Draw3dRect(rcItem, ::GetSysColor(COLOR_BTNFACE), 
		::GetSysColor(COLOR_BTNFACE));
	
	if (!IsWindowEnabled())
	{
		return;
	}

	switch (dwStyle)
	{
	case FCB_DRAWNORMAL:
		rcItem.top -= 1;
		rcItem.bottom += 1;
		pDC->Draw3dRect(rcItem, ::GetSysColor(COLOR_BTNHIGHLIGHT), 
			::GetSysColor(COLOR_BTNHIGHLIGHT));
		rcItem.left -= 1;
		pDC->Draw3dRect(rcItem, ::GetSysColor(COLOR_BTNHIGHLIGHT), 
			::GetSysColor(COLOR_BTNHIGHLIGHT));
		break;

	case FCB_DRAWRAISED:
		rcItem.top -= 1;
		rcItem.bottom += 1;
		pDC->Draw3dRect(rcItem, ::GetSysColor(COLOR_BTNHIGHLIGHT), 
			::GetSysColor(COLOR_BTNSHADOW));
		break;

	case FCB_DRAWPRESSD:
		rcItem.top -= 1;
		rcItem.bottom += 1;
		rcItem.OffsetRect(1, 1);
		pDC->Draw3dRect(rcItem, ::GetSysColor(COLOR_BTNSHADOW), 
			::GetSysColor(COLOR_BTNHIGHLIGHT));
		break;
	}

	ReleaseDC(pDC);
}

void CFontComboBox::OnLButtonDown(UINT nFlags, CPoint point) 
{
	m_bLBtnDown = TRUE;
	CComboBox::OnLButtonDown(nFlags, point);
}

void CFontComboBox::OnLButtonUp(UINT nFlags, CPoint point) 
{
	m_bLBtnDown = FALSE;
	Invalidate();
	CComboBox::OnLButtonUp(nFlags, point);
}

void CFontComboBox::OnMouseMove(UINT nFlags, CPoint point) 
{
	SetTimer(1, 10, NULL);
	CComboBox::OnMouseMove(nFlags, point);
}

void CFontComboBox::OnPaint() 
{
	Default();
	DrawCombo(FCB_DRAWNORMAL, ::GetSysColor(COLOR_BTNFACE), 
		::GetSysColor(COLOR_BTNFACE));
}

void CFontComboBox::OnTimer(UINT nIDEvent) 
{
	POINT pt;
	GetCursorPos(&pt);
	CRect rcItem;
	GetWindowRect(&rcItem);

	static bool bPainted = FALSE;

	// OnLButtonDown, show pressed.
	if (m_bLBtnDown)
	{
		KillTimer (1);
		if (bPainted)
		{
			DrawCombo(FCB_DRAWPRESSD, ::GetSysColor(COLOR_BTNSHADOW), 
				::GetSysColor(COLOR_BTNHIGHLIGHT));
			bPainted = FALSE;
		}
		return;
	}

	// If mouse leaves, show flat.
	if (!rcItem.PtInRect(pt))
	{
		KillTimer (1);
		if (bPainted)
		{
			DrawCombo(FCB_DRAWNORMAL, ::GetSysColor(COLOR_BTNFACE), 
				::GetSysColor(COLOR_BTNFACE));
			bPainted = FALSE;
		}
		return;
	}

	// On mouse over, show raised.
	else
	{
		if (bPainted)
			return;
		else
		{
			bPainted = TRUE;
			DrawCombo(FCB_DRAWRAISED, ::GetSysColor(COLOR_BTNSHADOW), 
				::GetSysColor(COLOR_BTNHIGHLIGHT));
		}
	}
	
	CComboBox::OnTimer(nIDEvent);
}